﻿using System.Reflection;
using System.Text;
using System.Xml;

namespace ConsoleApp1
{
    /// <summary>
    /// 微信xml字符串与实体的互转（支持嵌套类）
    /// </summary>
    public class WeChatXmlHelper
    {
        /// <summary>
        /// 微信xml字符串转实体
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="xml">微信xml字符串</param>
        /// <returns></returns>
        public static T? Parse<T>(string xml) where T : new()
        {
            var doc = new XmlDocument();
            doc.LoadXml(xml);
            var element = doc.DocumentElement;
            if (element != null)
            {
                return ParseNode<T>(element);
            }
            return default(T);
        }

        /// <summary>
        /// 微信实体转xml字符串
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="obj">实体</param>
        /// <returns></returns>
        public static string ToXml<T>(T obj)
        {
            var sb = new StringBuilder();
            using (var writer = XmlWriter.Create(sb, new XmlWriterSettings
            {
                OmitXmlDeclaration = true,
                Indent = true
            }))
            {
                writer.WriteStartElement("xml");
                WriteObject(writer, obj);
                writer.WriteEndElement();
            }
            return sb.ToString();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="node"></param>
        /// <returns></returns>
        private static T ParseNode<T>(XmlNode node) where T : new()
        {
            var obj = new T();
            var props = typeof(T).GetProperties();

            foreach (XmlNode child in node.ChildNodes)
            {
                var prop = Array.Find(props, p => string.Equals(p.Name, child.Name, StringComparison.OrdinalIgnoreCase));

                if (prop != null)
                {
                    object? value = GetValueFromNode(child, prop.PropertyType);
                    if (value != null)
                    {
                        prop.SetValue(obj, value);
                    }
                }
            }
            return obj;
        }

        /// <summary>
        /// 获取节点内容
        /// </summary>
        /// <param name="node">节点</param>
        /// <param name="targetType">节点类型</param>
        /// <returns></returns>
        private static object? GetValueFromNode(XmlNode node, Type targetType)
        {
            string? value = node.FirstChild?.NodeType == XmlNodeType.CDATA ? node.FirstChild.Value : node.InnerText;

            if (targetType == typeof(string)) return value;
            if (targetType.IsClass && targetType != typeof(string))
            {
                return typeof(WeChatXmlHelper).GetMethod("ParseNode", BindingFlags.NonPublic | BindingFlags.Static)?.MakeGenericMethod(targetType).Invoke(null, new object[] { node });
            }
            return Convert.ChangeType(value, targetType);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="obj"></param>
        private static void WriteObject(XmlWriter writer, object? obj)
        {
            if (obj == null) return;

            foreach (var prop in obj.GetType().GetProperties())
            {
                var value = prop.GetValue(obj);
                if (value == null) continue;

                writer.WriteStartElement(prop.Name);
                if (prop.PropertyType.IsClass && prop.PropertyType != typeof(string))
                    WriteObject(writer, value);
                else
                    writer.WriteCData(value.ToString());
                writer.WriteEndElement();
            }
        }
    }
}
